import os
import json
from PyQt6.QtCore import QObject, pyqtSignal, QThread, QMutex, QWaitCondition
from collections import deque, defaultdict
from datetime import datetime
import pandas as pd
from typing import Dict, List, Optional, Any
import queue


class ExcelWriterWorker(QThread):
    """Excel写入工作线程 - 常驻后台，基于队列的任务处理"""
    
    # 信号定义
    writer_message = pyqtSignal(str)    # 传递写入完成或者错误信息
    
    def __init__(self):
        super().__init__()
        self._task_queue = queue.Queue()
        self._running = True
        
    def add_write_task(self, recorded_data: Dict[str, List[Dict]], file_path: str) -> None:
        """添加写入任务到队列"""
        self._task_queue.put((recorded_data, file_path))
    
    def run(self) -> None:
        """主工作循环 - 处理队列中的写入任务"""
        while self._running:
            try:
                # 阻塞等待任务，避免CPU轮询
                recorded_data, file_path = self._task_queue.get(timeout=0.1)
                
                # 执行写入
                self._write_excel(recorded_data, file_path)
                self.writer_message.emit(f"数据已保存到: {file_path}")
                
                # 标记任务完成
                self._task_queue.task_done()
                
            except queue.Empty:
                # 队列为空，继续等待
                continue
            except Exception as e:
                self.writer_message.emit(str(e))
    
    def _write_excel(self, recorded_data: Dict[str, List[Dict]], file_path: str) -> None:
        """实际的Excel写入逻辑"""
        if not recorded_data:
            raise ValueError("没有数据可写入")
        
        # 确保目录存在
        os.makedirs(os.path.dirname(file_path), exist_ok=True)
        
        # 写入Excel文件
        with pd.ExcelWriter(file_path, engine='openpyxl') as writer:
            for identifier, data_list in recorded_data.items():
                if not data_list:
                    continue
                df = pd.DataFrame(data_list)
                df.to_excel(writer, sheet_name=identifier, index=False)
    
    def stop(self) -> None:
        """停止工作线程"""
        self._running = False
        # 等待当前任务完成
        self._task_queue.join()
        self.wait()  # 等待线程结束


class DataService(QObject):
    """
    数据服务类 - 职责单一：数据缓存和录制
    
    接收数据格式：{identifier: str, data: {fieldname: value}}
    缓存数据格式：{identifier: {field: deque}}
    录制数据格式：{identifier: [data_dict, ...]}
    """

    message_signal = pyqtSignal(str)

    def __init__(self, data_save_dir: str = 'data', chart_json_path: str = 'config/chart.json'):
        super().__init__()

        # 数据保存目录
        self._data_save_dir = data_save_dir
        os.makedirs(self._data_save_dir, exist_ok=True)

        # 解析配置，获取每个identifier的最大缓存长度
        self._identifier_maxlen = self._parse_identifier_maxlen(chart_json_path)
        
        # 数据缓存：{identifier: {field: deque}}
        self._data_buffer: Dict[str, Dict[str, deque]] = {}
        
        # 录制状态和数据
        self._recording = False
        self._recorded_data: Dict[str, List[Dict[str, Any]]] = defaultdict(list)
        
        # 启动常驻Excel写入工作线程
        self._excel_writer = ExcelWriterWorker()
        self._excel_writer.writer_message.connect(self._on_writer_message)
        self._excel_writer.start()

    def _parse_identifier_maxlen(self, chart_json_path: str) -> Dict[str, int]:
        """
        解析chart.json配置文件，获取每个identifier的最大数据点数
        
        Args:
            chart_json_path: 配置文件路径
            
        Returns:
            Dict[str, int]: {identifier: max_data_points}
        """
        identifier_maxlen = {}
        
        try:
            with open(chart_json_path, 'r', encoding='utf-8') as f:
                chart_cfg = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError) as e:
            self.message_signal.emit(f"配置文件读取失败: {e}")
            return {}
        
        # 遍历所有图表配置
        for tab_cfg in chart_cfg.values():
            for chart_key, chart_cfg_item in tab_cfg.items():
                if not chart_key.startswith('chart'):
                    continue
                
                identifier = chart_cfg_item.get('data_source')
                data_points = chart_cfg_item.get('data_points')
                
                if not identifier or not isinstance(data_points, int):
                    continue
                
                # 取同一数据源的最大data_points值
                identifier_maxlen[identifier] = max(
                    identifier_maxlen.get(identifier, 0), 
                    data_points
                )
        
        return identifier_maxlen

    def on_data_received(self, result: Dict[str, Any]) -> None:
        """
        接收数据回调函数
        
        Args:
            result: 数据结果，格式为 {identifier: str, data: {fieldname: value}, error: str}
        """
        # 错误处理
        error = result.get('error')
        if error:
            self.message_signal.emit(f"数据接收错误: {error}")
            return
        
        # 提取数据
        identifier = result.get('identifier')
        data = result.get('data')
        
        if not identifier or not isinstance(data, dict):
            self.message_signal.emit("数据格式错误: 缺少identifier或data字段")
            return
        
        # 初始化identifier的缓存
        if identifier not in self._data_buffer:
            self._data_buffer[identifier] = {}
        
        # 将数据添加到缓存
        for fieldname, value in data.items():
            if fieldname not in self._data_buffer[identifier]:
                maxlen = self._identifier_maxlen.get(identifier, 1000)
                self._data_buffer[identifier][fieldname] = deque(maxlen=maxlen)
            
            self._data_buffer[identifier][fieldname].append(value)
        
        # 如果正在录制，保存数据
        if self._recording:
            self._recorded_data[identifier].append(data.copy())

    def get_data(self, identifier: str) -> Optional[Dict[str, deque]]:
        """
        获取指定identifier的缓存数据
        
        Args:
            identifier: 数据标识符
            
        Returns:
            Optional[Dict[str, deque]]: 缓存数据，格式为 {field: deque}，不存在则返回None
        """
        return self._data_buffer.get(identifier)

    def start_record(self) -> None:
        """开始录制数据"""
        self._recording = True
        self._recorded_data.clear()

    def stop_record_and_save(self) -> Optional[str]:
        """
        停止录制并保存数据到Excel文件
        
        Returns:
            Optional[str]: 保存的文件路径，如果没有数据则返回None
        """
        self._recording = False
        
        # 生成文件路径
        timestamp = datetime.now().strftime('%Y%m%d_%H-%M-%S')
        file_path = os.path.join(self._data_save_dir, f'{timestamp}.xlsx')
        
        return self._save_recorded_data(file_path)
    
    def stop_record_and_save_with_name(self, filename: str) -> Optional[str]:
        """
        停止录制并使用指定文件名保存数据
        
        Args:
            filename: 文件名（会自动添加.xlsx扩展名）
            
        Returns:
            Optional[str]: 保存的文件路径，如果没有数据则返回None
        """
        self._recording = False
        
        # 确保文件扩展名
        if not filename.endswith('.xlsx'):
            filename += '.xlsx'
        
        file_path = os.path.join(self._data_save_dir, filename)
        return self._save_recorded_data(file_path)
    
    def _save_recorded_data(self, file_path: str) -> Optional[str]:
        """
        异步保存录制的数据到文件
        
        Args:
            file_path: 文件路径
            
        Returns:
            Optional[str]: 立即返回文件路径，实际写入在后台进行
        """
        # 复制数据并清空当前录制
        recorded_data_copy = dict(self._recorded_data)
        self._recorded_data.clear()
        
        if not recorded_data_copy:
            self.message_signal.emit("没有数据可保存")
            return None
        
        # 提交写入任务到工作线程队列
        self._excel_writer.add_write_task(recorded_data_copy, file_path)
        
        # 立即返回路径，不阻塞主线程
        return file_path
    
    def _on_writer_message(self, message: str) -> None:
        """处理写入线程的消息"""
        self.message_signal.emit(message)
    
    def __del__(self):
        """确保线程在对象销毁时正确停止"""
        if hasattr(self, '_excel_writer'):
            self._excel_writer.stop()
    
